package logger

import (
	"bufio"
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/gin-gonic/gin"
	"github.com/lestrrat-go/file-rotatelogs"
	"github.com/pkg/errors"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"os"
	"path"
	"path/filepath"
	"time"
)

type LogCallback func(filename string) error

// Loginfo ...
type Loginfo struct {
	Dir   string
	Name  string
	Age   int
	Level string
}

// InitLogger ...
func InitLogger(loginfo *Loginfo) error {

	if loginfo == nil {
		return nil
	}

	baseLogPath := "log/log"
	linkfile := "file.log"
	if len(loginfo.Name) > 0 && len(loginfo.Dir) > 0 {
		baseLogPath = path.Join(loginfo.Dir, loginfo.Name)
		linkfile = loginfo.Name + ".log"
	}

	age := 7 * 24 * time.Hour
	if loginfo.Age != 0 {
		age = time.Duration(loginfo.Age) * 24 * time.Hour
	}

	writer, err := rotatelogs.New(
		baseLogPath+".%Y%m%d%H%M",
		rotatelogs.WithLinkName(linkfile),         // 生成软链，指向最新日志文件
		rotatelogs.WithMaxAge(age),                // 文件最大保存时间
		rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔
	)

	if err != nil {
		logrus.Errorf("config local file system logger error. %v", errors.WithStack(err))
		return err
	}

	lfHook := lfshook.NewHook(lfshook.WriterMap{
		logrus.DebugLevel: writer,
		logrus.InfoLevel:  writer,
		logrus.WarnLevel:  writer,
		logrus.ErrorLevel: writer,
		logrus.FatalLevel: writer,
		logrus.PanicLevel: writer},
		//&logrus.JSONFormatter{},
		&logrus.TextFormatter{},
	)
	logrus.AddHook(lfHook)

	SetLogLevel(loginfo.Level)

	return nil

}

// GinLogger ...
func GinLogger(c *gin.Context) {
	start := time.Now()
	// 处理请求
	c.Next()
	// 结束时间
	end := time.Now()
	//执行时间
	latency := end.Sub(start)
	path := c.Request.URL.Path
	clientIP := c.ClientIP()
	method := c.Request.Method
	statusCode := c.Writer.Status()
	// 这里是指定日志打印出来的格式。分别是状态码，执行时间,请求ip,请求方法,请求路由(等下我会截图)
	logrus.Infof("| %3d | %13v | %15s | %s  %s |",
		statusCode,
		latency,
		clientIP,
		method, path,
	)
}

// SetLogLevel ...
func SetLogLevel(level string) {

	logrus.Warnf("Setloglevel %v ==> %v ", logrus.GetLevel().String(), level)

	ll, err := logrus.ParseLevel(level)
	if err != nil {
		setNull()
		logrus.SetLevel(logrus.InfoLevel)
	}

	logrus.SetLevel(ll)
	if ll == logrus.TraceLevel {
		logrus.SetOutput(os.Stderr)
	} else {
		setNull()
	}
}

// TestLogLevel ...
func TestLogLevel() {
	logrus.Trace("TestLogLevel Trace")
	logrus.Debug("TestLogLevel debug")
	logrus.Info("TestLogLevel Info")
	logrus.Warn("TestLogLevel Warn")
	logrus.Error("TestLogLevel Error")
	//logrus.Fatal("TestLogLevel Fatal")
}

func setNull() {
	src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
	if err != nil {
		fmt.Println("err", err)
	}
	writer := bufio.NewWriter(src)
	logrus.SetOutput(writer)
}

func WatchConfigfile(confile string, logcb LogCallback) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		logrus.Error(err)
		return err
	}
	// defer watcher.Close()
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					_, fileName := filepath.Split(confile)
					if event.Name == fileName {
						//LoadLogConfig("")
						//if LogConfigFile.Log.Level != logrus.GetLevel().String() {
						//	utils.SetLogLevel(LogConfigFile.Log.Level)
						//}
						logcb(confile)
					}
				}
			case err := <-watcher.Errors:
				logrus.Warnf("error: %v", err)
			}
		}
	}()

	err = watcher.Add(confile)
	if err != nil {
		logrus.Error(err)
		return err
	}

	return nil
}
